import os
from typing import List, Union, Iterable, Callable, Dict

from script.downloader.Downloader import Downloader
from script.Parameter import Parameter


class ParserParameter(Parameter):
    _s_instance = None

    def __init__(self):
        super(ParserParameter, self).__init__()

        self._input_list: List[str] = []
        self._output_dir: str = ''

        self._parser = {
            '-h': lambda params: ParserParameter._help(),
            '--help': lambda params: ParserParameter._help(),
            '-u': lambda params: self._parse_url(params),
            '--url': lambda params: self._parse_url(params),
            '-i': lambda params: self._parse_input_list(params),
            '--input_list': lambda params: self._parse_input_list(params),
            '-o': lambda params: self._parse_output_dir(params),
            '--output_dir': lambda params: self._parse_output_dir(params)
        }

    @staticmethod
    def get():
        if ParserParameter._s_instance is None:
            ParserParameter._s_instance = ParserParameter()
        return ParserParameter._s_instance

    def input_list(self) -> Iterable[str]:
        return self._input_list

    def output_dir(self) -> str:
        return self._output_dir

    def _on_begin(self) -> Dict[str, Callable[[Iterable[str]], int]]:
        return self._parser

    def _on_end(self) -> bool:
        return len(self._input_list) > 0

    @staticmethod
    def _help() -> int:
        print('help')
        return 0

    def _parse_url(self, params: [Iterable[str]]) -> int:
        if len(params) <= 0:
            return -1

        # save url to input list
        self._input_list.append(params[0])

        # 1 parameter consumed
        return 1

    def _parse_input_list(self, params: [Iterable[str]]) -> int:
        if len(params) <= 0:
            return -1

        # check whether file exists
        if not os.path.exists(params[0]) or not os.path.isfile(params[0]):
            return -1

        # load input list file
        with open(params[0], 'r') as file:
            for line in file.readlines():
                line = line.strip()
                if len(line) == 0:
                    continue
                self._input_list.append(line)

        # 1 parameter consumed
        return 1

    def _parse_output_dir(self, params: [Iterable[str]]) -> int:
        if len(params) <= 0:
            return -1

        # verify output dir
        if os.path.exists(params[0]) and not os.path.isdir(params[0]):
            print('output dir cannot be created')
            return -1

        # save output dir
        self._output_dir = params[0]

        # 1 parameter consumed
        return 1


class Main(object):
    def __init__(self):
        self._param = ParserParameter.get()
        self._valid = self._param.parse()

        self._last_progress = -1

        if not self._valid:
            print('Invalid parameter')

    def valid(self):
        return self._valid

    @staticmethod
    def _on_download_progress(cur: int, total: int):
        if total > 0:
            print('%d/%d' % (cur, total))
            # progress = cur // (total / 100)
            # if progress > self._last_progress:
            #     print('%d/%d' % (progress, 100))
        else:
            print(cur)

    def main(self):
        input_list = self._param.input_list()
        output_dir = self._param.output_dir()

        for url in input_list:
            downloader = Downloader.create(url, output_dir)
            if downloader is None:
                print('> Ignore invalid url: %s' % url)
                continue

            print('> Download [%s] ...' % url)
            result, out_path, real_url = downloader.download(Main._on_download_progress)

            if result:
                print('> Done\n'
                      '    -> out: %s\n'
                      '    -> real: %s' % (out_path, real_url))
            else:
                print('Error: %s' % downloader.last_error())


if __name__ == '__main__':
    Main().main()
